package org.ourgrid.common.executor.vbox;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.ourgrid.common.executor.config.ExecutorConfiguration;
import org.ourgrid.common.executor.config.VBoxExecutorConfiguration;
import org.ourgrid.common.util.OS;
import org.ourgrid.worker.WorkerConfiguration;
import org.ourgrid.worker.WorkerConstants;

public class VBoxCommandFactory {
	
	// scripts
	private String startScript;
	private String stopScript;
	
	//VBox settings
	private String vBoxLocation;
	private String machineName;
	
	//Virtual Machine Environment
	private String sharedFolder;
	private String playpenDir;
	private String storageDir;
	private String VMplaypenDir;
	private String VMstorageDir;
	private String stdOutPutFileInVM;
	private String errOutPutFileInVM;
	private String exitValueFileInVM;
	private String executionScript;
	
	private String stdOutPutFile;
	private String errOutPutFile;
	private String exitValueFile;
	
	//
	private String redirectSimbol = "> ";
	private String endOfCommandSimbol = " ; ";
	private String quotes = "\"";
	private String setExecutablePermission = "chmod -R 700 ";
	
	public VBoxCommandFactory(ExecutorConfiguration executorConfiguration){
		init(executorConfiguration);
	}
	
	public  void init(ExecutorConfiguration executorConfiguration){
		
		VMplaypenDir = "$PLAYPEN";
		VMstorageDir = "$STORAGE";
		
		this.vBoxLocation = executorConfiguration
				.getProperty(WorkerConfiguration.PREFIX + VBoxExecutorConfiguration.PROPERTIES.PROPERTY_VBOX_LOCATION.toString());
		this.machineName = executorConfiguration
				.getProperty(WorkerConfiguration.PREFIX + VBoxExecutorConfiguration.PROPERTIES.PROPERTY_VBOX_MACHINE_NAME.toString());
		this.sharedFolder = executorConfiguration
				.getProperty(WorkerConfiguration.PREFIX + VBoxExecutorConfiguration.PROPERTIES.PROPERTY_SHARED_FOLDER.toString());
		
		this.playpenDir = sharedFolder.substring(1, sharedFolder.length() - 1) + File.separator +"playpen"; 
		this.storageDir = sharedFolder.substring(1, sharedFolder.length() - 1) + File.separator +"storage"; 
		
		this.startScript = executorConfiguration
				.getProperty(VBoxExecutorConfiguration.PROPERTIES.PROPERTY_START_VBOX_COMMAND.toString());
		this.stopScript = executorConfiguration
				.getProperty(VBoxExecutorConfiguration.PROPERTIES.PROPERTY_STOP_VBOX_COMMAND.toString());
		
		this.stdOutPutFileInVM = VMplaypenDir + "/" + executorConfiguration
				.getProperty(VBoxExecutorConfiguration.PROPERTIES.APP_STD_OUTPUT_FILE_NAME.toString());
		this.errOutPutFileInVM = VMplaypenDir + "/" + executorConfiguration
				.getProperty(VBoxExecutorConfiguration.PROPERTIES.APP_STD_ERROR_FILE_NAME.toString());
		this.exitValueFileInVM = VMplaypenDir + "/" + executorConfiguration
				.getProperty(VBoxExecutorConfiguration.PROPERTIES.APP_STD_EXITVALUE_FILE_NAME.toString());
		this.executionScript = playpenDir + File.separator + executorConfiguration
				.getProperty(VBoxExecutorConfiguration.PROPERTIES.APP_STD_EXECUTION_SCRIPT_FILE_NAME.toString());
		
		this.stdOutPutFile = "app.stdout";
		this.errOutPutFile = "app.stderr";
		this.exitValueFile = "app.exit";
		
		
	}
	
	public List<String> createInitCommand(){
		List<String> initCommand = new LinkedList<String>();
		
		initCommand.add(startScript);
		initCommand.add(vBoxLocation);
		initCommand.add(machineName);
		initCommand.add(sharedFolder);
		
		return initCommand;
	}
	
	public void createExecutionScriptCmd(String cmd,  Map<String, String> envVars) throws IOException{
		
		String runScriptCommand = getCompleteTaskCommand(cmd, envVars);
		
		File script = new File(executionScript);
		File outPut = new File(playpenDir, stdOutPutFile);
		File error = new File(playpenDir, errOutPutFile);
		File exitValue = new File(playpenDir, exitValueFile);
		
		makeExecutionScript(script, runScriptCommand);
		makeExecutionScript(outPut, "");
		makeExecutionScript(error, "");
		makeExecutionScript(exitValue, "");
	}
	
	private void makeExecutionScript(File file, String execCom) throws IOException {
		PrintWriter writer = new PrintWriter(new FileWriter(file));
		writer.write(execCom);
		writer.flush();
		writer.close();
	}

	private String getCompleteTaskCommand(String command, Map<String, String> envVars) {
		
		Map<String, String> clone = new HashMap<String, String>();
		clone.putAll(envVars);
        
		clone.remove(WorkerConstants.ENV_PLAYPEN);
		clone.remove(WorkerConstants.ENV_STORAGE);
		clone.put(WorkerConstants.ENV_PLAYPEN, VMplaypenDir);
		clone.put(WorkerConstants.ENV_STORAGE, VMstorageDir);
		
		String exportVariablesCommand = createExportVariablesCommand(clone); 
		
		String finalCommand = "";
		finalCommand = "echo " + quotes + exportVariablesCommand + 
					createSettingFilesInDirAsExecutableCommand(VMstorageDir) +
					createSettingFilesInDirAsExecutableCommand(VMplaypenDir) +
					"cd" + " " + VMplaypenDir + endOfCommandSimbol + command + quotes + redirectSimbol + 
					VMplaypenDir + "/script" + endOfCommandSimbol + getRunScriptCommand();
		return finalCommand;
	}
	
	private String createSettingFilesInDirAsExecutableCommand(String filePath) {
		return setExecutablePermission + filePath + endOfCommandSimbol;
	}

	private String createExportVariablesCommand(Map<String, String> envVars) {
		String command = "";
		String exportCommand = "export ";
		
		if ( envVars != null ) {
			if ( !envVars.isEmpty() ) {
				for (String key : envVars.keySet()) {
					command += key + "=\'" + envVars.get( key ) + "\'" + endOfCommandSimbol
					+ exportCommand + key + endOfCommandSimbol;
				}
			}
		}
		return command;
	}

	private String getRunScriptCommand() {
		return "sh " + VMplaypenDir + "/script" + 
			   " 1" + redirectSimbol + stdOutPutFileInVM +
			   " 2" + redirectSimbol +  errOutPutFileInVM + endOfCommandSimbol + 
			   "echo $?" + redirectSimbol + exitValueFileInVM;
	}
	
	public List<String> createStopCommand(){
		List<String> stopCommand = new LinkedList<String>();
		
		stopCommand.add(stopScript);
		stopCommand.add(vBoxLocation);
		stopCommand.add(machineName);
		
		return stopCommand;
	}
	
}